#pragma once
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string>

#define MAX_CLIENT 5
#define MAX_SIZE 1024
const socklen_t sizeof_addr_in = sizeof(sockaddr_in);

class TcpSocket{
  public:
    TcpSocket():socket_fd(-1){}
    TcpSocket(int fd):socket_fd(fd){}

    bool CreateSocket(){
      socket_fd = socket(AF_INET, SOCK_STREAM, 0);
      int opt = 1;
      setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
      if(socket_fd < 0){
        perror("socket");
        return false;
      }
      return true;
    }

    bool Bind(const std::string& ip, uint16_t port){
      sockaddr_in myaddr;
      myaddr.sin_family = AF_INET;
      inet_aton(ip.c_str(), &myaddr.sin_addr);
      myaddr.sin_port = htons(port);
      int ret = bind(socket_fd, (sockaddr*)&myaddr, sizeof_addr_in);
      if(ret < 0){
        perror("bind");
        return false;
      }
      return true;
    }

    bool Listen(int BackLog = MAX_CLIENT){
      int ret = listen(socket_fd, BackLog);
      if(ret < 0){
        perror("listen");
        return false;
      }
      return true;
    }

    bool Connect(std::string& svr_ip, uint16_t svr_port){
      sockaddr_in svr_addr;
      svr_addr.sin_family = AF_INET;
      inet_aton(svr_ip.c_str(), &svr_addr.sin_addr);
      svr_addr.sin_port = htons(svr_port);
      int ret = connect(socket_fd, (sockaddr*)&svr_addr, sizeof_addr_in);
      if(ret < 0){
        perror("connect");
        return false;
      }
      return true;
    }

    bool Accept(TcpSocket& cli, sockaddr_in& cli_addr){
      socklen_t len = sizeof_addr_in;
      int new_sock = accept(socket_fd, (sockaddr*)&cli_addr, &len);
      if(new_sock < 0){
        perror("accept");
        return false;
      }
      cli.socket_fd = new_sock;
      return true;
    }
    
    bool Recv(std::string& buf) const {
      buf.clear();
      char tmp[MAX_SIZE] = {0};
      ssize_t recv_size = recv(socket_fd, tmp, MAX_SIZE - 1, 0);
      if(recv_size < 0){
        perror("recv");
        return false;
      }
      if(recv_size == 0){
        return false;
      }
      buf.assign(tmp, recv_size);
      return true;
    }

    bool Send(const std::string& buf) const {
      ssize_t send_size = send(socket_fd, buf.data(), buf.size(), 0); 
      if(send_size < 0){
        perror("send");
        return false;
      }
      if(send_size == 0){
        printf("The connection has been closed\n");
        return false;
      }
      return true;
    }

    void Close(){
      close(socket_fd);
    }
  private:
    int socket_fd;
};
